package javax.per.toolkit.pool;

import javax.per.toolkit.pool.Task.TaskPriority;
import javax.per.toolkit.pool.Task.TaskState;


public class ThreadPool {
	private int maxThreadNum;
	private int minThreadNum=0;
	private int threadNum=0;
	private int increasement=5;
	private int threadForImmediatelyNum=10;
	private int sleepTime=10;
	
	private enum PoolState{INIT,RUNING,START_SHUTDOWN,STOP,WAITING_SHUTDOWN}
	PoolState poolState=null;
	
	private int extraThreadImmediatelyCount=0;
	
	private TaskQueue<Task> taskQueue_High=new TaskQueue<Task>();
	private TaskQueue<Task> taskQueue_Medium=new TaskQueue<Task>();
	private TaskQueue<Task> taskQueue_Low=new TaskQueue<Task>();
	
	private TaskQueue<ExecuteThread> threadListNotUsed=new TaskQueue<ExecuteThread>();
	private TaskQueue<ExecuteThread> threadListInUse=new TaskQueue<ExecuteThread>();
	private TaskQueue<ExecuteThread> threadListForImmediatelyNotUsed=new TaskQueue<ExecuteThread>();
	private TaskQueue<ExecuteThread> threadListForImmediatelyInUse=new TaskQueue<ExecuteThread>();
	
	private DispatcherThread dispatcherThread;
	
	public PoolState getPoolState() {
		return poolState;
	}

	private boolean canProcessTask=false;
	private boolean canAddTask=false;
	private boolean canIncrease=false;
	private boolean isIncreasing=false;
	
	
	
	public ThreadPool(int maxThreadNum){
		this.maxThreadNum=maxThreadNum;
		this.init();
	}
	
	public ThreadPool(int minThreadNum,int maxThreadNum){
		this.maxThreadNum=maxThreadNum;
		this.minThreadNum=minThreadNum;
		this.init();
	}
	
	public ThreadPool(int minThreadNum,int maxThreadNum,int threadForImmediatelyNum){
		this.threadForImmediatelyNum=threadForImmediatelyNum;
		this.maxThreadNum=maxThreadNum;
		this.minThreadNum=minThreadNum;
		this.init();
	}
	
	private void init(){
		this.poolState=PoolState.INIT;
		
		this.canAddTask=true;
		this.canIncrease=true;
		this.canProcessTask=true;
		
		this.threadNum=this.minThreadNum;
		for(int i=0;i<this.threadNum;i++){
			ExecuteThread th=new ExecuteThread();
			th.start();
			this.threadListNotUsed.addLast(th);
		}
		for(int i=0;i<this.threadForImmediatelyNum;i++){
			ExecuteThread th=new ExecuteThread();
			th.start();
			this.threadListForImmediatelyNotUsed.addLast(th);
		}
		
		this.poolState=PoolState.RUNING;
		
		dispatcherThread=new DispatcherThread();
		dispatcherThread.start();
	}
	
	
	private Task getFirstTask(){
		if(!this.canProcessTask)
			return null;
			if(this.taskQueue_High.size()!=0){
				return this.taskQueue_High.pollFirst();
			}else if(this.taskQueue_Medium.size()!=0){
				return this.taskQueue_Medium.pollFirst();
			}else if(this.taskQueue_Low.size()!=0){
				return this.taskQueue_Low.pollFirst();
			}else{
				return null;
			}
	}
	
	
	private boolean increase(){
		
		
		this.isIncreasing=true;
		if(!this.canIncrease){
			
			this.isIncreasing=false;
			return false;
		}
		if(this.threadNum+this.increasement<=this.maxThreadNum){
			for(int i=0;i<this.increasement;i++){
				ExecuteThread th=new ExecuteThread();
				th.start();
				this.threadListNotUsed.addLast(th);
			}
			this.threadNum+=this.increasement;
			this.isIncreasing=false;
			System.err.println("increase-------->");
			return true;
		}else if(this.threadNum<this.maxThreadNum){
			for(int i=this.threadNum+1;i<=this.maxThreadNum;i++){
				ExecuteThread th=new ExecuteThread();
				th.start();
				this.threadListNotUsed.addLast(th);
			}
			this.threadNum=this.maxThreadNum;
			this.isIncreasing=false;
			System.err.println("increase-------->");
			return true;
		}
		
		this.isIncreasing=false;
		return false;
	}
	
	private void addTask(Task task){
		task.setState(TaskState.RUNNABLE);
		switch(task.getPriority()){
		case HIGH_PRIORITY:
			taskQueue_High.addLast(task);
			break;
		case LOW_PRIORITY:
			taskQueue_Low.addLast(task);
			break;
		case MEDIUM_PRIORITY:
			taskQueue_Medium.addLast(task);
			break;
		case RUN_IMMEDIATELY:
			runImmediately(task);
			break;
		default:
			throw new Error();
		}
	}
	
	
	synchronized private void runImmediately(Task task){
		if(this.canProcessTask){
			if(!this.threadListForImmediatelyNotUsed.isEmpty()){
				this.threadListForImmediatelyNotUsed.pollFirst().putTask(task);
			}else{
				new ImmediatelyRunThread(task).start();
			}
		}
	}
	
	public void submit(Task task){
		if(!this.canAddTask)
			return;
		System.err.println(this.taskQueue_Medium.size());
		addTask(task);
	}
	
	private void addTaskFirst(Task task){
		task.setState(TaskState.RUNNABLE);
		switch(task.getPriority()){
		case HIGH_PRIORITY:
			taskQueue_High.addFirst(task);
			break;
		case LOW_PRIORITY:
			taskQueue_Low.addFirst(task);
			break;
		case MEDIUM_PRIORITY:
			taskQueue_Medium.addFirst(task);
			break;
		default:
			throw new Error();
		}
	}
	
	private boolean canShutdown(){
		if(this.poolState==PoolState.STOP||this.poolState==PoolState.START_SHUTDOWN)
			return false;
		else
			return true;
	}
	
	private boolean shutdownNeedWait(){
		if(this.poolState==PoolState.INIT||this.isIncreasing)
			return true;
		else
			return false;
	}
	
	private boolean isFinished(){
		if(!this.taskQueue_High.isEmpty()||!this.taskQueue_Low.isEmpty()||!this.taskQueue_Medium.isEmpty())
			return false;
		if(!this.threadListForImmediatelyInUse.isEmpty()||!this.threadListInUse.isEmpty())
			return false;
		if(this.extraThreadImmediatelyCount!=0)
			return false;
		return true;
	}
	
	@SuppressWarnings("deprecation")
	public void shutdownImmediately(){
		if(!this.canShutdown())
			return;
		while(this.shutdownNeedWait()){
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.poolState=PoolState.START_SHUTDOWN;
		
		this.canAddTask=false;
		this.canProcessTask=false;
		this.canIncrease=false;
		
		while(!this.threadListInUse.isEmpty()){
			this.threadListInUse.pollFirst().stop();
		}
		while(!this.threadListForImmediatelyInUse.isEmpty()){
			this.threadListForImmediatelyInUse.pollFirst().stop();
		}
		
		this.threadListForImmediatelyNotUsed.clear();
		this.threadListInUse.clear();
		this.dispatcherThread.stop();
		
		this.poolState=PoolState.STOP;
	}
	
	@SuppressWarnings("deprecation")
	public void shutdownWait(){
		System.err.println(this.poolState.toString());
		if(!this.canShutdown())
			return;
		
		while(this.shutdownNeedWait()){
			this.sleep();
		}
		
		this.poolState=PoolState.WAITING_SHUTDOWN;
		
		this.canAddTask=false;
		this.canProcessTask=true;
		this.canIncrease=true;
		
		while(!this.isFinished()){
			this.printState();
			sleep();
		}
		
		
		
		this.canProcessTask=false;
		this.canIncrease=false;
		this.canAddTask=false;
		
		this.poolState=PoolState.START_SHUTDOWN;
		
		this.threadListForImmediatelyNotUsed.clear();
		this.threadListInUse.clear();
		
		this.dispatcherThread.stop();
		this.poolState=PoolState.STOP;
	}
	
	private void sleep(){
		try {
			Thread.sleep(sleepTime);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public void restart(){
		
	}
	
	private void printState(){
		System.err.println("high:"+this.taskQueue_High.size()+"\nmedium:"+this.taskQueue_Medium.size()+"\nlow:"+this.taskQueue_Low.size());
		System.err.println("used:"+this.threadListInUse.size()+"\nimme used:"+this.threadListForImmediatelyInUse.size()+"extra:"+this.extraThreadImmediatelyCount+"\n");
	}
	
	private class DispatcherThread extends Thread{

		@Override
		public void run() {
				Task task=null;
				while(canProcessTask){
					
					while((task=getFirstTask())!=null){
						if(canProcessTask){
							if(!threadListNotUsed.isEmpty()){
								threadListNotUsed.pollFirst().putTask(task);
								continue;
							}else if(increase()){
								threadListNotUsed.pollFirst().putTask(task);
							}else{
									addTaskFirst(task);
									break;
							}
						}else{
							addTaskFirst(task);
							break;
						}
					}
					try {
						Thread.sleep(sleepTime);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
		}
		
	}
	
	
	private class ExecuteThread extends Thread{
		private Task task=null;
		@SuppressWarnings("deprecation")
		public void putTask(Task task){
			this.task=task;
			if(task.getPriority()!=TaskPriority.RUN_IMMEDIATELY){
				threadListInUse.addLast(this);
			}else{
				threadListForImmediatelyInUse.addLast(this);
			}
			this.resume();
		}
		@SuppressWarnings("deprecation")
		@Override
		public void run() {
			while(canProcessTask){
				if(task!=null){
					
					task.setState(TaskState.RUNING);
					task.run();
					task.setState(TaskState.DEAD);
					if(task.getPriority()!=TaskPriority.RUN_IMMEDIATELY){
						threadListNotUsed.addLast(this);
						if(threadListInUse.remove(this)==null)
							System.err.println("eoor");
					}
					else{
						threadListForImmediatelyNotUsed.addLast(this);
						threadListForImmediatelyInUse.remove(this);
					}
					task=null;
					
				}else{
					this.suspend();
				}
			}
			
		}
		
	}
	
	private class ImmediatelyRunThread extends Thread{
		
		private Task task;
		
		public ImmediatelyRunThread(Task task){
			this.task=task;
		}

		@Override
		public void run() {
			extraThreadImmediatelyCount++;
			task.setState(TaskState.RUNING);
			task.run();
			task.setState(TaskState.DEAD);
			extraThreadImmediatelyCount--;
		}
	}
	
	

}
